package filereaders;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;


/**
 * 
 * @author Paul Dannenberg
 * 
 */

public abstract class Parser {

    private static final int CALENDAR_MONTH_INDEX = 2;
    private static final int CALENDAR_DAY_INDEX = 4;
    private static final CharSequence DATE_DELIMETER = "/";
    private final String myFileName;

    /**
     * Loads a parser for a particular filename.
     * The parser is specific to the file.
     * 
     * @param filename The filename that this 
     * parser will read.
     */
    public Parser(String filename) {
        myFileName = filename;
    }

    /**
     * 
     * @return A scanner for the particular file
     *         that this Parser can read.
     */
    public Scanner loadScanner() {
        File fileToRead = new File(myFileName);
        Scanner sc = null;
        try {
            sc = new Scanner(fileToRead);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            // TODO HANDLE ERROR PROPERLY!!!
        }
        return sc;
    }

    /**
     * 
     * @return The filename that this Parser can
     *         read.
     */
    public String getFileName() {
        return myFileName;
    }

    /**
     * Returns an object containing all the formatted values of a particular
     * field.
     * 
     * @param fieldName A specifier for a particular field that data should
     *        be gathered from.Some values may have been reformatted. Any non
     *        numeric,
     *        non-decimalpoint will be removed. Dates will be converted to a
     *        yymmdd format.
     * @return A Collection of all values associated with the argument
     *         fieldName.
     *         If no such fieldName exists in the file to be read, this method
     *         returns
     *         an empty Collection.
     * 
     */
    public abstract Collection<Double> parseField(String fieldName);

    /**
     * Given a keyField and valueField, returns a mapping from the keyField to
     * the
     * valueField. For example, this may map a particular date to a particular
     * opening price. Some values may have been reformatted. Any non numeric,
     * non-decimalpoint will be removed. Dates will be converted to a
     * yymmdd format.
     * 
     * @param keyField The data heading of the map key to be returned. e.g.
     *        "date"
     * @param valueField The value that the keyField should map to. e.g.
     *        "opening".
     * @return A map mapping the data specified by the keyField to the data
     *         specified
     *         by the valueField. If one of the fields specified in the argument
     *         does not exist in the file to be parsed, this method returns an
     *         empty Map.
     */

    public abstract Map<Double, Double> parseRelatedFields(String keyField,
            String valueField);

    protected boolean isWord(String input) {
        return Pattern.matches("^[a-zA-Z]+$", input);
    }

    protected boolean isInteger(String input) {
        return Pattern.matches("^[0-9]+$", input);
    }

    protected boolean isDouble(String input) {
        return Pattern.matches("\\d+\\.\\d+", input) || isInteger(input);
    }

    protected double formatDataToDouble(String dataPiece) {
        if (isDate(dataPiece)) {
            return Double.parseDouble(removeNonNumerics(formatDate(dataPiece)));
        }
        if (isDouble(dataPiece)) {
            return Double.parseDouble(dataPiece);
        }
        String formattedWord = removeNonNumerics(dataPiece);
        if ("".equals(formattedWord) || ".".equals(formattedWord)) {
            return 0;
        }
        return Double.parseDouble(formattedWord);
    }

    protected String formatDate(String dataPiece) {
        String month = "";
        String day = "";
        String year = "";
        for (int i = 0; i < dataPiece.length(); i++) {
            if (i < CALENDAR_MONTH_INDEX) {
                month += Character.toString(dataPiece.charAt(i));
            } 
            else if (i <= CALENDAR_DAY_INDEX) {
                day += Character.toString(dataPiece.charAt(i));
            } 
            else {
                year += Character.toString(dataPiece.charAt(i));
            }
        }
        return year + month + day;
    }

    protected String removeNonNumerics(String toFormat) {
        String formattedWord = "";
        for (int i = 0; i < toFormat.length(); i++) {
            if (isInteger((Character.toString(toFormat.charAt(i))))
                    || '.' == toFormat.charAt(i)) {
                formattedWord += Character.toString(toFormat.charAt(i));
            }
        }
        return formattedWord;
    }

    private boolean isDate(String input) {
        return input.contains(DATE_DELIMETER);
    }
}
